/**
 * MIT License
 * 
 * Copyright (c) 2017 CaiDongyu
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.tunnel.client.task;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

import com.tunnel.client.config.Config;
import com.tunnel.client.config.IpAndPort;
import com.tunnel.client.tunnel.TunnelChannelHandlerContextManager;
import com.tunnel.common.constant.Constant;
import com.tunnel.common.http.HttpPack;
import com.tunnel.common.task.BaseTask;
import com.tunnel.common.tunnel.TunnelDataQueue;
import com.tunnel.common.tunnel.TunnelDataSenderManager;
import com.tunnel.common.util.HttpUtil;

import io.netty.channel.ChannelHandlerContext;

/**
 * Tunnel与客户服务之间的通讯任务
 * 在tunnel-client接受到来自tunnel-server的请求之后
 */
public class HttpTask extends BaseTask{

	private int hostIndex;
	private String requestId;
	private byte[] requestIdBytes;
	private byte[] dataBytes;
	
	public void cancelTask(){
//		LogUtil.d("task need stop: "+requestId);
		super.setStop(true);
	}
	
	public String getRequestId() {
		return requestId;
	}
	
	public HttpTask(int hostIndex,byte[] requestIdBytes,byte[] dataBytes) {
		super();
		this.hostIndex = hostIndex;
		requestId = new String(requestIdBytes);
		this.requestIdBytes = requestIdBytes;
		this.dataBytes = dataBytes;
		this.setName("HttpTask-"+requestId);
	}

	@Override
	public void run() {
//		LogUtil.d(requestId+" content: "+new String(dataBytes));
		
		//发送回到服务端
		ChannelHandlerContext ctx = TunnelChannelHandlerContextManager.getChannelHandlerContext();
		if(ctx != null){
			//解析完数据接口
			//开始查找转发地址，转发数据
			if(hostIndex < Config.HOST_LIST.size()){
				IpAndPort ipAndPort = Config.HOST_LIST.get(hostIndex);
				
				Socket socket = new Socket();
				ByteArrayOutputStream byteBuf = new ByteArrayOutputStream();
				try {
					socket.connect(new InetSocketAddress(ipAndPort.getIp(), ipAndPort.getPort()), 1000);//设置连接请求超时时间10 s
					socket.setSoTimeout(30000);//设置读操作超时时间30 s
					
					OutputStream out = socket.getOutputStream();
//					LogUtil.d("request:"+new String(dataBytes));
					out.write(dataBytes);
					out.flush();
					
					InputStream in = socket.getInputStream();
					byte[] cache = new byte[TunnelDataQueue.ITEM_SIZE()];//根据全局设置的tunnel通道发送速度进行包大小定义
					int len = 0;
					//至少先把http头读完，读完后后面还剩多少，就分批次回传，不等待in.read阻塞
					HttpPack httpPack = null;
					boolean headerIsSend = false;
					int lenTotal = 0;
					byte[] lastByteAry = null;
					
					while ((len = in.read(cache)) > 0 && !isStop()) {
						
//						LogUtil.d(requestId+" asr: "+new String(cache,0,len));
						lenTotal = lenTotal+len;
						if(httpPack == null || httpPack.getHeader() == null){
							byteBuf.write(cache, 0, len);
							byte[] data = byteBuf.toByteArray();
							httpPack = HttpUtil.analyzeHttpData(data);
							//这里千万注意，多判断一下，因为↑上一行语句只对byteBuf里的内容解析到header结束，
							//多出的内容没有解析，所以header里可能会少内容，一旦header解析到了，就把整体都存一下
							//分析header就是为了知道后面还有多长要取，够了就跳出去，不再等待in的最后阻塞
							if(httpPack != null && httpPack.getHeader() != null){
								boolean checkBodyIsOk = HttpUtil.checkBodyIsOk(httpPack, data);
								if(checkBodyIsOk){
									//如果实际解析header就已经是读取完了，那么就直接结束
//									LogUtil.d("tunnel-client返回："+new String(header.getHeader()));
									sendData2Server(ctx, requestIdBytes, httpPack.getHeader(), httpPack.getBody());
									headerIsSend = true;//跳出while读流后，必须保证不会再传一次header
									/*LogUtil.d("response_1:content-len["+httpPack.getContentLength());
									LogUtil.d("  head["+httpPack.getHeaderLength()+"]");
									if(httpPack.getBody() != null){
										LogUtil.d("  body["+httpPack.getBody().length+"]");
									}
									LogUtil.d("实际传输："+lenTotal);*/
									break;
								}
							}
						}else{
							byte[] data = new byte[len];
							System.arraycopy(cache, 0, data, 0, len);
							if(!headerIsSend){
								sendData2Server(ctx, requestIdBytes, httpPack.getHeader(),httpPack.getBody(),data);
								headerIsSend = true;//下次传就不带header了
							}else{
								sendData2Server(ctx, requestIdBytes, data);
							}
							
							boolean checkBodyIsOk = false;
							switch (httpPack.getContentLenType()) {
								case 1:
									checkBodyIsOk = lenTotal>=httpPack.getContentLength()+httpPack.getHeaderLength();
									/*if(lenTotal>httpPack.getContentLength()+httpPack.getHeaderLength()){
										LogUtil.d("more:"+(lenTotal-(httpPack.getContentLength()+httpPack.getHeaderLength())));
										LogUtil.d(new String(data));
									}*/
									break;
								case 2:
									if(lastByteAry != null){
										data = new byte[lastByteAry.length+len];
										System.arraycopy(lastByteAry, 0, data, 0, lastByteAry.length);
										System.arraycopy(cache, 0, data, lastByteAry.length, len);
										checkBodyIsOk = HttpUtil.checkBodyIsOk(httpPack, data);
									}else{
										checkBodyIsOk = HttpUtil.checkBodyIsOk(httpPack, cache);
									}
									break;
								case 3:
									checkBodyIsOk = false;//说明返回信息带connection:close
									break;
								default:
									//说明body不用考虑
									checkBodyIsOk = true;
									break;
							}
							if(checkBodyIsOk){
//								LogUtil.d("response_2:"+new String(httpPack.getHeader()));
//								LogUtil.d(lenTotal+"/"+httpPack.getContentLength()+"/"+httpPack.getHeader().length);
//								LogUtil.d("实际传输："+lenTotal);
								break;//达到数据传输长度了，就结束传输，不再等待in的阻塞
							}
						}
						
						//保存给下次使用，前两次都用不到，后面的话，需要组合前一次的读取结果，判定是否结尾
						lastByteAry = new byte[len];
						System.arraycopy(cache, 0, lastByteAry, 0, len);
						
						/*try {
							//根据消息队列的窗口期作为休息参考，单task下保证窗口期不过多的把数据提交给队列
							Thread.sleep(TunnelDataQueue.ITEM_SEND_DELAY_TIME);
						} catch (Exception e) {}*/
					}
					
					if(!headerIsSend){
						sendData2Server(ctx, requestIdBytes, httpPack.getHeader(),httpPack.getBody());
						headerIsSend = true;//下次传就不带header了
					}
					
					//发送一个我已经传完了的信号，告诉tunnel-server，已经结束了，tunnel-server就可以关闭浏览器了
//					LogUtil.d(requestId+" asr end.");
					sendData2Server(ctx, requestIdBytes, Constant.HTTP_RESPONSE_END_FLAG_BYTES);
					out.close();
				} catch (Exception e) {
					byte[] response500 = HttpUtil.response500(e.getMessage(),"tunnel-client");
					sendData2Server(ctx, requestIdBytes, response500);
				} finally {
					try {
						socket.close();
					} catch (Exception e2) {}
					try {
						byteBuf.close();
					} catch (Exception e2) {}
				}
			}else{
				byte[] response404 = HttpUtil.response404("tunnel-client");
				sendData2Server(ctx, requestIdBytes, response404);
			}
		}

		/*try {
			HttpTaskManager.stopTask(requestId);
		} catch (Exception e) {}*/
//		LogUtil.d("stop task："+requestId);
	}

	private void sendData2Server(ChannelHandlerContext ctx, byte[] ...data) {
		TunnelDataSenderManager.commitData(this,TunnelDataSenderManager.HTTP_DATA_MSG, ctx, data);
	}
	
}
